package com.blue.bluecode.sync;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;

import com.blue.bluecode.R;
import com.blue.bluecode.entity.Image;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Set;

/**
 * 带有缓存的图片下载类
 * 二级缓存(缓存在盘中)
 * Created by bluewaitor on 2015/4/13.
 */
public class ImageLoaderWithDoubleCaches {
    private Set<AsyncDownloadImage> mTasks;
    private LruCache<String, Bitmap> mMemoryCaches;
    private DiskLruCache mDiskLruCaches;
    private ListView mListView;

    public ImageLoaderWithDoubleCaches(Context context, ListView mListView) {
        this.mListView = mListView;
        mTasks = new HashSet<>();
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 10;

        mMemoryCaches = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }
        };

        File cacheDir = getFileCache(context, "disk_caches");
        if(!cacheDir.exists()){
            cacheDir.mkdir();
        }

        try {
            mDiskLruCaches = DiskLruCache.open(cacheDir, 1, 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private File getFileCache(Context context, String cacheFileName) {
        String cachePath;
        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()){
            cachePath = context.getExternalCacheDir().getPath();
        }else{
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + cacheFileName);
    }

    public void showImage(String url, ImageView imageView){
        Bitmap bitmap = getBitmapFromMemoryCaches(url);
        if(bitmap == null){
            imageView.setImageResource(R.mipmap.ic_launcher);
        }else{
            imageView.setImageBitmap(bitmap);
        }
    }

    public Bitmap getBitmapFromMemoryCaches(String url){
        return mMemoryCaches.get(url);
    }

    public void addBitmapToMemoryCaches(String url, Bitmap bitmap){
        if(getBitmapFromMemoryCaches(url) == null){
            mMemoryCaches.put(url, bitmap);
        }
    }

    public void loadImages(int start, int end){
        for(int i = start; i < end; i++){
            String url = Image.IMAGE_URLS[i];
            Bitmap bitmap = getBitmapFromMemoryCaches(url);
            if(bitmap == null){
                AsyncDownloadImage task = new AsyncDownloadImage(url);
                mTasks.add(task);
                task.execute(url);
            }else{
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }


    private static boolean getBitmapUrlToStream(String urlString, OutputStream outputStream){
        HttpURLConnection connection = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;

        try {
            final URL url = new URL(urlString);
            connection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(connection.getInputStream(), 8 * 1024);
            out = new BufferedOutputStream(outputStream, 8 * 1024);
            int b;
            while((b = in.read()) != -1){
                out.write(b);
            }
            return true;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if( connection != null){
                connection.disconnect();
            }

            try {
                if(out != null){
                    out.close();
                }
                if(in != null){
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    public void cancelAllTasks(){
        if(mTasks != null){
            for(AsyncDownloadImage task: mTasks){
                task.cancel(false);
            }
        }
    }

    public String toMD5String(String key){
        String cacheKey;
        try{
            final MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(key.getBytes());
            cacheKey = bytesToHexString(digest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < bytes.length; i++){
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if(hex.length() == 1){
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    public void flushCache(){
        if(mDiskLruCaches != null){
            try {
                mDiskLruCaches.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class AsyncDownloadImage extends AsyncTask<String, Void, Bitmap>{

        private String url;

        public AsyncDownloadImage(String url) {
            this.url = url;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            url = strings[0];
            FileDescriptor fileDescriptor = null;
            FileInputStream fileInputStream = null;
            DiskLruCache.Snapshot snapshot = null;
            String key = toMD5String(url);


            try {
                snapshot = mDiskLruCaches.get(key);
                if(snapshot == null){
                    DiskLruCache.Editor editor = mDiskLruCaches.edit(key);
                    if(editor != null){
                        OutputStream outputStream = editor.newOutputStream(0);
                        if(getBitmapUrlToStream(url, outputStream)){
                            editor.commit();
                        }else{
                            editor.abort();
                        }
                    }
                    snapshot = mDiskLruCaches.get(key);
                }
                if(snapshot != null){
                    fileInputStream = (FileInputStream) snapshot.getInputStream(0);
                    fileDescriptor = fileInputStream.getFD();
                }
                Bitmap bitmap = null;
                if(fileDescriptor != null){
                    bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
                }
                if(bitmap != null){
                    addBitmapToMemoryCaches(strings[0], bitmap);
                }
                return bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(fileDescriptor == null && fileInputStream != null){
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            ImageView imageView = (ImageView) mListView.findViewWithTag(url);
            if(imageView != null && bitmap != null){
                imageView.setImageBitmap(bitmap);
            }
            mTasks.remove(this);
        }
    }

}
